package com.jzo2o.market.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.market.dto.request.CouponUseBackReqDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.domain.CouponUseBack;
import com.jzo2o.market.model.domain.CouponWriteOff;
import com.jzo2o.market.model.dto.request.CouponLimitReqDTO;
import com.jzo2o.market.model.dto.request.CouponOperationPageQueryReqDTO;
import com.jzo2o.market.model.dto.request.SeizeCouponReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.CouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponUseBackService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.market.utils.CouponUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageUtils;
import com.jzo2o.redis.utils.RedisSyncQueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.jzo2o.common.constants.ErrorInfo.Code.SEIZE_COUPON_FAILD;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Resource(name = "seizeCouponScript")
    private DefaultRedisScript<String> seizeCouponScript;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponUseBackService couponUseBackService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;


    @Override
    public PageResult<CouponInfoResDTO> pageList(CouponOperationPageQueryReqDTO couponOperationPageQueryReqDTO) {
//        这个page是mybatisPlus的分页对象
        Page<Coupon> page = new Page<>(couponOperationPageQueryReqDTO.getPageNo(),
                couponOperationPageQueryReqDTO.getPageSize());

//    todo  这个查询的也是Foundatio里面的Coupon，而不是market的
        LambdaQueryWrapper<Coupon> query = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<Coupon> eq =
                query.eq(Coupon::getActivityId,
                        couponOperationPageQueryReqDTO.getActivityId());

        Page<Coupon> result = baseMapper.selectPage(page,eq);
        return PageUtils.toPage(result, CouponInfoResDTO.class);

    }

    @Override
    public void updateStatus(Long id) {
        LambdaUpdateWrapper<Coupon> couponLambdaUpdateWrapper =
                new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<Coupon> query = couponLambdaUpdateWrapper
                .eq(Coupon::getActivityId, id)
                .eq(Coupon::getStatus,
                        CouponStatusEnum.NO_USE.getStatus())
                .set(Coupon::getStatus, CouponStatusEnum.VOIDED.getStatus());
        baseMapper.update(null, query);
    }

    @Override
    public List<CouponInfoResDTO> myCoupons(CouponLimitReqDTO couponLimitReqDTO) {
        LambdaUpdateWrapper<Coupon> wrapper =
                new LambdaUpdateWrapper<>();

        LambdaUpdateWrapper<Coupon> last =
                wrapper.eq(Coupon::getStatus, couponLimitReqDTO.getStatus())
                        .eq(ObjectUtils.isNotEmpty(couponLimitReqDTO.getUserId()),
                                Coupon::getUserId,couponLimitReqDTO.getUserId())
                .lt(ObjectUtils.isNotEmpty(couponLimitReqDTO.getLastId()),
                        Coupon::getId,
                        couponLimitReqDTO.getLastId())
                .orderByDesc(Coupon::getId)
                .last("limit 10");
        List<Coupon> coupons = baseMapper.selectList(last);
        List<CouponInfoResDTO> couponInfoResDTOS = BeanUtils.copyToList(coupons, CouponInfoResDTO.class);
        return couponInfoResDTOS;


    }

    @Override
    public void useExprieCoupon() {
        LambdaUpdateWrapper<Coupon> objectLambdaUpdateWrapper =
                new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<Coupon> query = objectLambdaUpdateWrapper
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .lt(Coupon::getValidityTime, LocalDateTime.now())
                .set(Coupon::getStatus, CouponStatusEnum.INVALID.getStatus());
        baseMapper.update(null, query);
    }

    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupon(Long totalAmount, Long userId) {
        LambdaQueryWrapper<Coupon> objectLambdaUpdateWrapper = new LambdaQueryWrapper<>();
        Long id = UserContext.currentUser().getId();
        LambdaQueryWrapper<Coupon> query = objectLambdaUpdateWrapper
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus()).
                gt(Coupon::getValidityTime, LocalDateTime.now())
                .lt(Coupon::getAmountCondition, totalAmount)
                .eq(Coupon::getUserId, id)
                .orderByDesc(Coupon::getAmountCondition);
        List<Coupon> coupons = baseMapper.selectList(query);
        if (CollUtils.isEmpty(coupons)) {
            return CollUtils.emptyList();
        }
        List<AvailableCouponsResDTO> collect = coupons.stream()
                .peek(coupon -> coupon.setDiscountAmount(
                        CouponUtils.calDiscountAmount(coupon, new BigDecimal(totalAmount))))
                .filter(coupon -> coupon.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0)
                .map(coupon -> BeanUtils.toBean(coupon, AvailableCouponsResDTO.class))
                .collect(Collectors.toList());
        return collect;

    }

    @Override
    public CouponUseResDTO use(CouponUseReqDTO couponUseReqDTO) {
        if (ObjectUtil.isNull(couponUseReqDTO.getOrdersId())||
                ObjectUtil.isNull(couponUseReqDTO.getTotalAmount())) {
            throw new BadRequestException("订单信息不存在!");
        }
        Coupon coupon = baseMapper.selectById(couponUseReqDTO.getId());
        if (ObjectUtil.isEmpty(coupon)) {
            throw new BadRequestException("优惠券不存在!");
        }
        if (ObjectUtil.notEqual(UserContext.currentUser().getId(), coupon.getUserId())) {
            throw new CommonException("只能核销本人的优惠卷");
        }
        LambdaUpdateWrapper<Coupon> couponLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
//        更新优惠卷的状态 并且要满足优惠券使用条件
        LambdaUpdateWrapper<Coupon> query = couponLambdaUpdateWrapper
                .eq(Coupon::getId, coupon.getId())
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .eq(Coupon::getUserId, UserContext.currentUser().getId())
                .gt(Coupon::getValidityTime, LocalDateTime.now())
                .set(Coupon::getStatus, CouponStatusEnum.INVALID.getStatus())
                .set(Coupon::getUseTime, LocalDateTime.now())
                .lt(Coupon::getAmountCondition, couponUseReqDTO.getTotalAmount())
                .set(Coupon::getOrdersId, couponUseReqDTO.getOrdersId());
        int update = baseMapper.update(null, query);
        if(update<=0)
        {
            throw new DBException("优惠券核销失败");
        }

        CouponWriteOff build = CouponWriteOff.builder()
                .couponId(coupon.getId())
                .activityId(coupon.getActivityId())
                .id(IdUtils.getSnowflakeNextId())
                .userId(UserContext.currentUser().getId())
                .ordersId(couponUseReqDTO.getOrdersId())
                .writeOffManPhone(coupon.getUserPhone())
                .writeOffManName(coupon.getUserName())
                .writeOffTime(LocalDateTime.now())
                .build();

        boolean save = couponWriteOffService.save(build);
        if (!save) {
            throw new DBException("优惠券核销失败");
        }


        BigDecimal bigDecimal = CouponUtils.
                calDiscountAmount(coupon, couponUseReqDTO.getTotalAmount());
        CouponUseResDTO couponUseResDTO = CouponUseResDTO.builder()
                .discountAmount(bigDecimal)
                .build();
        return couponUseResDTO;


    }


}
